 //
//  QGNetworkManager.swift
//  斯威夫特微博
//
//  Created by youyoumacmini3 on 2017/7/7.
//  Copyright © 2017年 youyoumacmini3. All rights reserved.
//

import UIKit
import AFNetworking
 
 // swift 的枚举支持任意数据类型
 // switch / enum 在OC 中都只支持整数
 
/*
  如果日常开发中，发现网络请求返回的状态码是 405 ，不支持的网络请求方法
  首先应该查找网络请求方法是否正确
*/
 
 
enum QGHTTPMethod {
    case GET
    case POST
}
 
/// 网络管理工具
class QGNetworkManager: AFHTTPSessionManager {

    // 静态区 / 常量 / 闭包 
    // 在第一次访问时，执行闭包，并且将结果保存在 shared 常量中
    static let shared: QGNetworkManager = {
       
        //实例化对象
        let instance = QGNetworkManager()
        
        //设置响应反序列化支持的数据格式类型
        instance.responseSerializer.acceptableContentTypes?.insert("text/plain")
        
        //返回对象
        return instance
    }()
    
    // 用户账户的懒加载属性
    lazy var userAccount = QGUserAccount()
    
    
    // 用户登录标记（计算型属性）
    var userLogin:Bool {
        return userAccount.access_token != nil
    }
    
    /// 专门负责拼接 token 的网络请求方法
    ///
    /// - Parameters:
    ///   - method: GET/POST
    ///   - URLString: URLString
    ///   - parameters: 参数字典
    ///   - name: 上传文件的字段名，默认为nil，就不是上传文件
    ///   - data: 上传文件的二进制数据，默认为nil，就不是上传文件
    ///   - completion: 回调
    func tokenRequest(method: QGHTTPMethod = .GET, URLString: String, parameters:[String: Any]?, name: String? = nil, data: Data? = nil, completion:@escaping (Any?,Bool)->()) {
        
        // 处理 token 字典
        // 0. 判断 token 是否为 nil ，为 nil 直接返回
        // 程序执行过程中 token 一般不会为 nil
        guard let token = userAccount.access_token else {
        
            // 发送通知，提示用户的登录
            print("没有 token 需要登录")
            
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: QGUserShouldLoginNotification), object: nil)
            
            
            completion(nil, false)
            return
        }
        
        
        // 1. 判断参数字典是否存在，如果为nil, 应该新建一个字典
        var parameters = parameters
        
        if parameters == nil {
            //实例化字典
            parameters = [String: Any]()
        }
        
        // 2. 设置参数字典,代码在此处 字典一定有值
        parameters!["access_token"] = token
        
        // 3. 判断 name 和 data
        if let name = name,let data = data {
            // 上传文件
            upload(URLString: URLString, name: name, data: data, parameters: parameters, completion: completion)
        }else{
            // 调用 request 发起真正的网络请求方法
            //        request(URLString: URLString, parameters: parameters, completion: completion)
            request(method: method, URLString: URLString, parameters: parameters, completion: completion)
        }
        

    }
    
    //MARK: - 封装 AFN 方法
    // 上传文件必须是 POST； GET只能获取数据
    /// 封装 AFN 上传文件方法
    ///
    /// - Parameters:
    ///   - URLString: URLString
    ///   - name: 接收上传数据的服务器字段（name：要咨询公司后台）
    ///   - data: 要上传的二进制数据
    ///   - parameters: 参数字典
    ///   - completion: 完成回调
    func upload(URLString: String, name: String, data: Data, parameters:[String: Any]?, completion:@escaping (Any?,Bool)->()) {
        
        post(URLString, parameters: parameters, constructingBodyWith: { (formData) in
            
            // FIXME: - 创建 formData
            /*
             1. data: 要上传的二进制数据
             2. name: 服务器接收数据的字段名
             3. fileName: 保存在服务器的文件名，大多数服务器，现在可以乱写，很多服务器，上传图片完成后会生成缩略图，中图，大图
             4. mimeType: 告诉服务器上传文件的类型，如果不想告诉，可以使用application/octet-stream (image/png,image/jpg.. )
             */
            formData.appendPart(withFileData: data, name: name, fileName: "xxx", mimeType: "application/octet-stream")
            
        }, progress: nil, success: { (_, json) in
            
            completion(json, true)
            
        }) { (task, error) in
            if (task?.response as? HTTPURLResponse)?.statusCode == 403 {
                print("token 过期了")
                
                // 发送通知，提示用户再次登录（本方法不知道被谁调用，谁接收通知，谁处理）
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: QGUserShouldLoginNotification), object: "bad token")
                
            }
            completion(nil, false)
        }
    }
    
    
    /// 封装AFN get/post 请求
    ///
    /// - Parameters:
    ///   - method: GET/POST
    ///   - URLString: URL
    ///   - parameters: 参数字典
    ///   - completion: 完成回调[json(字典/数组)，是否成功]
    func request(method: QGHTTPMethod = .GET, URLString: String, parameters:[String: Any]?, completion:@escaping (Any?,Bool)->()) {
      
        /// 成功回调
        let success = { (task: URLSessionDataTask, json: Any?)->() in
            completion(json, true)
        }
        
        /// 失败回调
        let failure = { (task: URLSessionDataTask?, error: Error)->() in
            print("网络请求错误\(error)")
            
            // 针对 403 处理用户 token 过期
            // HTTPURLResponse:URLResponse 父类转子类（用as转）用来找statusCode，
            if (task?.response as? HTTPURLResponse)?.statusCode == 403 {
                print("token 过期了")
                
                // 发送通知，提示用户再次登录（本方法不知道被谁调用，谁接收通知，谁处理）
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: QGUserShouldLoginNotification), object: "bad token")
                
            }
            
            
            completion(nil, false)
            
        }
        
        
        
        if method == .GET {
            
            get(URLString, parameters: parameters, progress: nil, success: success, failure: failure)
            
        }else{
            
            post(URLString, parameters: parameters, progress: nil, success: success, failure: failure)
        }
        
        
        
        
        
    }
    
    
    
}
